// SysInit Unit
// Modular Support - DLL

(*************)
(* Interface *)
(*************)
{$IFDEF Include_Modules_Header}

 Procedure _InitLib;

 // ==== Safe unloading support ====
 // * This is for DLL to be used in non-Delphi applications only. Delphi application have its own
 //    mechanism to automatically signal unload safety.
 // Background:
 //  In the current implementation, if the main program exits without explicitly freeing the DLL,
 //   the RTL will assume it is a failure exit, and will not execute normal unloading procedures.
 //  This behavior is desirable because if the program is really in a crash exit phase, the memory
 //   or other resources may not be in a sane state, and doing more stuff can cause more troubles.
 //  However, in case the DLL is statically loaded, and the program actually is exiting normally
 //   we need to know it is still safe to perform unloading procedures, and hence this mechanism.
 // Usage:
 //  1. In the DLL - export the RTL_ModuleSafeUnload procedure;
 //  2. In the application - call the RTL_ModuleSafeUnload before normal exit.
 Procedure RTL_ModuleSafeUnload;

{$ENDIF Include_Modules_Header}
(******************)
(* Implementation *)
(******************)
{$IFDEF Include_Modules_Implement}

 ResourceString
  _RTL_ResStr_ContextDLLMain     = 'DLL Entry Function - ';
  _RTL_ResStr_DLLProcAttach      = 'Process Attach';
  _RTL_ResStr_DLLProcDetach      = 'Process Detach';
  _RTL_ResStr_DLLThreadAttach    = 'Thread Attach';
  _RTL_ResStr_DLLThreadDetach    = 'Thread Detach';

 Const
  _INT_DLL_PROCESS_DETACH = 0;
  _INT_DLL_PROCESS_ATTACH = 1;
  _INT_DLL_THREAD_ATTACH  = 2;
  _INT_DLL_THREAD_DETACH  = 3;

  _INT_TLSProc: Array [_INT_DLL_PROCESS_DETACH.._INT_DLL_THREAD_DETACH] of TProc =
   (
    _INT_ExitProcessTLS,
    _INT_InitProcessTLS,
    _INT_InitThreadTLS,
    _INT_ExitThreadTLS
   );

  _INT_DLLProcName: Array [_INT_DLL_PROCESS_DETACH.._INT_DLL_THREAD_DETACH] of PResStringRec =
  (
   @_RTL_ResStr_DLLProcDetach,
   @_RTL_ResStr_DLLProcAttach,
   @_RTL_ResStr_DLLThreadAttach,
   @_RTL_ResStr_DLLThreadDetach
  );

 Procedure RTL_ModuleSafeUnload;
  BEGIN
   System._RTL_SafeShutdown:= True;
  END;

 Procedure _INT_LogEntryException(Const Context: PResStringRec; Reason: Integer);
  Var
   ErrContext: Array [0..RTL_ResStr_MsgLen+1] of Char;
   MsgLen: Cardinal;
  BEGIN
   MsgLen:= LoadResStringFx(Context, ErrContext, RTL_ResStr_MsgLen+1);
   ErrContext[MsgLen]:= ' '; Inc(MsgLen);
   LoadResStringFx(_INT_DLLProcName[Reason], @ErrContext[MsgLen], RTL_ResStr_MsgLen-MsgLen+1);
   System.LogException(ErrContext);
   // If we are just a library, display error message (otherwise we know the delphi application will handle the failure)
   IF System.IsLibrary then System.NotifyError;
  END;

 Function _INT_InitLib(Const ModuleInfo: TModuleInfo; AHInst: NativeUInt; Reason: Integer; Reserved: Pointer): LongBool; StdCall;
  Procedure DoDLLAttach;
   BEGIN
    Try
     IF @TLSLast <> NIL then _INT_TLSProc[Reason];
     System.RegisterModule(@_INT_Module);
     System.ModuleInit(@_INT_Module);
    Except
     _INT_LogEntryException(@_RTL_ResStr_ContextDLLMain, Reason);
     Result:= False;
    END;
   END;

  Procedure DoDLLDetach;
   BEGIN
    Try
     System.ModuleFInit(@_INT_Module);
     System.UnregisterModule(@_INT_Module);
     IF @TLSLast <> NIL then _INT_TLSProc[Reason];
    Except
     _INT_LogEntryException(@_RTL_ResStr_ContextDLLMain, Reason);
    END;
   END;

  Procedure DoThreadTLS;
   BEGIN
    Try
     IF @TLSLast <> NIL then _INT_TLSProc[Reason];
    Except
     _INT_LogEntryException(@_RTL_ResStr_ContextDLLMain, Reason);
     Raise;
    END;
   END;

  BEGIN
   Result:= True;

   Case Reason of
    _INT_DLL_PROCESS_ATTACH:
     BEGIN
      System._RTL_FirstChanceInit;

      _INT_ModuleType:= mtDLL;
      _INT_HInstance:= AHInst;
      _INT_Module.Instance:= AHInst;
      _INT_Module.ModuleInfo:= @ModuleInfo;
      _INT_InitExtensionStore;

      DoDLLAttach;
     END;

    _INT_DLL_PROCESS_DETACH:
     // If Reserved is assigned, then the current process is being terminated,
     // and thus no further action should be performed, unless it is a signaled safe shutdown.
     IF NOT Assigned(Reserved) OR System._RTL_SafeShutdown
     then DoDLLDetach;

    else DoThreadTLS;
   END;
  END;

 // This is actually *NOT* the DLL entry point, there exists a thin outer "shell" entry point
 //  which loads the address of ModuleInfo structure into EAX.
 Procedure _InitLib;
 // (DLLMain RetAddr)     [ESP]
 // Saved EBX             [ESP+4]
 // (PInitContext)        [EBP-20]
 // (Exception Registry)  [EBP-4]..[EBP-16]
 // Saved EBP             [EBP]
 // OSRetAddr             [EBP+4]
 // AHInst                [EBP+8]
 // Reason                [EBP+12]
 // Reserved              [EBP+16]
  ASM
   MOV  ESP, EBP
   POP  EBP

   // We don't want to invoke _Halt0 which was hardcoded into the compiler
   // So we just directly return to the OS
   // Warning: The BEGIN ... END section of the library will NOT be executed
   POP  ECX
   PUSH EAX
   PUSH ECX
 // OSRetAddr    [ESP]
 // ModuleInfo   [ESP+4]
 // AHInst       [EBP+8]
 // Reason       [EBP+12]
 // Reserved     [EBP+16]
   JMP _INT_InitLib
  END;

{$ENDIF Include_Modules_Implement}

